#!/usr/bin/env python

"""
Test PySlip 'point in polygon' function.

We assume that any point right on a corner or edge may be inside *or*
outside the polygon.  This won't matter for pySlip.
"""

import unittest
import time

import pyslip


class TestPIP(unittest.TestCase):

    def test_simple(self):
        """Test some very simple cases"""

        # define a unit square polygon, not closed
        poly = [(0,0), (1,0), (1,1), (0,1)]

        # point in middle of simple polygon
        x, y = 0.5, 0.5
        self.failUnless(pyslip.point_inside_polygon(x, y, poly))

        # point very close to top-right corner, but inside
        x, y = 0.999999999, 0.999999999
        self.failUnless(pyslip.point_inside_polygon(x, y, poly))

        # point very close to top-left corner, but inside
        x, y = 0.000000001, 0.999999999
        self.failUnless(pyslip.point_inside_polygon(x, y, poly))

        # point very close to bottom-left corner, but inside
        x, y = 0.000000001, 0.000000001
        self.failUnless(pyslip.point_inside_polygon(x, y, poly))

        # point very close to bottom-right corner, but inside
        x, y = 0.999999999, 0.000000001
        self.failUnless(pyslip.point_inside_polygon(x, y, poly))

        # point just outside top-right corner, check all 3 quadrants
        x, y = 1.000000001, 1.0
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = 1.0, 1.000000001
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = 1.000000001, 1.000000001
        self.failIf(pyslip.point_inside_polygon(x, y, poly))

        # point just outside top-left corner, check all 3 quadrants
        x, y = -0.000000001, 1.0
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = 0.0, 1.000000001
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = -0.000000001, 1.000000001
        self.failIf(pyslip.point_inside_polygon(x, y, poly))

        # point just outside bottom-left corner, check all 3 quadrants
        x, y = -0.000000001, 0.0
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = 0.0, -0.000000001
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = -0.000000001, -0.000000001
        self.failIf(pyslip.point_inside_polygon(x, y, poly))

        # point just outside bottom-right corner, check all 3 quadrants
        x, y = 1.000000001, 0.0
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = 1.0, -0.000000001
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = 1.000000001, -0.000000001
        self.failIf(pyslip.point_inside_polygon(x, y, poly))

        # check some points *really* outside the poly
        x, y = 100.0, 0.5
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = 0.5, 100.0
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = -100.0, 0.5
        self.failIf(pyslip.point_inside_polygon(x, y, poly))
        x, y = 0.5, -100.0
        self.failIf(pyslip.point_inside_polygon(x, y, poly))

    def test_paranoid_quadrants(self):
        """Some more paranoid tests in different quadrants."""

        # define a unit square polygon, not closed
        poly = [(0.0,0.0), (1.0,0.0), (1.0,1.0), (0.0,1.0)]

        # point in middle of simple polygon, displaced
        x, y = 0.5, 0.5

        base = (1.0, 1.0)
        self.helper_test(poly, x, y, base, True)

        base = (-2.0, 1.0)
        self.helper_test(poly, x, y, base, True)

        base = (-2.0, -2.0)
        self.helper_test(poly, x, y, base, True)

        base = (1.0, -2.0)
        self.helper_test(poly, x, y, base, True)

        base = (-0.5, -0.5)
        self.helper_test(poly, x, y, base, True)

        # define a complex polygon (bow-tie)
        poly = [(0.0,0.0), (1.0,1.0), (0.0,1.0), (1.0,0.0)]

        # point in bow-tie polygon, displaced
        x, y = 0.5, 0.25

        base = (1.0, 1.0)
        self.helper_test(poly, x, y, base, True)

        base = (-2.0, 1.0)
        self.helper_test(poly, x, y, base, True)

        base = (-2.0, -2.0)
        self.helper_test(poly, x, y, base, True)

        base = (1.0, -2.0)
        self.helper_test(poly, x, y, base, True)

        base = (-0.5, -0.5)
        self.helper_test(poly, x, y, base, True)

        # point outside bow-tie polygon, displaced
        x, y = 0.500000001, 0.5

        base = (1.0, 1.0)
        self.helper_test(poly, x, y, base, False)

        base = (-2.0, 1.0)
        self.helper_test(poly, x, y, base, False)

        base = (-2.0, -2.0)
        self.helper_test(poly, x, y, base, False)

        base = (1.0, -2.0)
        self.helper_test(poly, x, y, base, False)

        # point outside bow-tie polygon, displaced
        x, y = 0.499999999, 0.5

        base = (1.0, 1.0)
        self.helper_test(poly, x, y, base, False)

        base = (-2.0, 1.0)
        self.helper_test(poly, x, y, base, False)

        base = (-2.0, -2.0)
        self.helper_test(poly, x, y, base, False)

        base = (1.0, -2.0)
        self.helper_test(poly, x, y, base, False)

        base = (-0.5, -0.5)
        self.helper_test(poly, x, y, base, False)

    def test_paranoid_reentrant(self):
        """Reentrant tests in different quadrants."""

        # define a unit square polygon, not closed, reentrant
        poly = [(0.0,0.0), (0.0,1.0), (1.0,1.0), (1.0,0.25), (0.75,0.25),
                (0.75,0.75), (0.25,0.75), (0.25,0.25), (1.0,0.25), (1.0,0.0)]

        # point in the bowels, but outside
        x, y = 0.5, 0.749999999

        base = (1.0, 1.0)
        self.helper_test(poly, x, y, base, False)

        base = (-2.0, 1.0)
        self.helper_test(poly, x, y, base, False)

        base = (-2.0, -2.0)
        self.helper_test(poly, x, y, base, False)

        base = (1.0, -2.0)
        self.helper_test(poly, x, y, base, False)

        base = (-0.5, -0.5)
        self.helper_test(poly, x, y, base, False)

        # point outside
        x, y = 1.000000001, 0.1

        base = (1.0, 1.0)
        self.helper_test(poly, x, y, base, False)

        base = (-2.0, 1.0)
        self.helper_test(poly, x, y, base, False)

        base = (-2.0, -2.0)
        self.helper_test(poly, x, y, base, False)

        base = (1.0, -2.0)
        self.helper_test(poly, x, y, base, False)

        base = (-0.5, -0.5)
        self.helper_test(poly, x, y, base, False)

    def helper_test(self, rel_poly, x, y, base, expected):
        """A test helper.

        x         x coord of test point, displaced by 'base'
        y         y coord of test point, displaced by 'base'
        rel_poly  a relative polygon, displaced by 'base': [(x1,y1), ...]
        base      a tuple (x_offset, y_offset)
        expected  expected 'in polygon' value, True or False

        Each point in the polygon and test point are displaced by 'base'.
        """

        (base_x, base_y) = base

        # displace the relative polygon and test point
        test_poly = []
        for (rx, ry) in rel_poly:
            test_poly.append((rx+base_x, ry+base_y))

        test_x = x + base_x
        test_y = y + base_y

        result = pyslip.point_inside_polygon(test_x, test_y, test_poly)
        self.failUnless(result == expected)

################################################################################

if __name__ == '__main__':
    suite = unittest.makeSuite(TestPIP,'test')
    runner = unittest.TextTestRunner()
    runner.run(suite)
